{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# An Overview of the PythonCOM & Win32COM Libraries"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When a new installation of Python is placed in a system, there aren't many Windows-specific functionalities that come along with it. To increase the array of tools in your arsenal, a set of extensions were developed to fill in the missing pieces; these extensions came to be known as the Python for Windows Extensions.\n",
    "\n",
    "With these extensions, we can now access different window-specific features like creating and running COM objects, direct access to the Win32 API, Windows GUI, and much more. In this tutorial, we will focus on one of two of these extensions, the PythonCOM and Win32COM library.\n",
    "\n",
    "***\n",
    "\n",
    "## Overview of COM Objects\n",
    "Being that we will be working with COM objects throughout this tutorial, it is vital to have an idea of how they work and what they are. Microsoft introduced the Component Object Model (COM) in 1993; this new framework would allow the reuse of objects (components) regardless of the language they were implemented in. For example, I might write an object in C++, but I would be able to use and interact with it inside of another programming language, like Python.\n",
    "\n",
    "This is one of the best features of a COM object; we can write objects in one language and use them in another without having to know anything about how they were implemented. How is this achieved you may ask? Through the use of an **interface**.\n",
    "\n",
    "Microsoft does a great job of explaining an interface, so let's leverage what they have. **An interface defines a set of methods that an object can support, without dictating anything about the implementation.** The interface marks a clear boundary between code that calls a method and the code that implements the method. **In computer science terms, the caller is decoupled from the implementation.**\n",
    "\n",
    "***\n",
    "\n",
    "## An Introduction to the Win32 COM Library\n",
    "Have you ever wanted to access different applications from Python? For example, maybe you have some data in Excel that would like to load into Python for further manipulation. To the surprise of many, the entire VBA model is accessible from Python. However, to access this model, we need to access it by using COM objects.\n",
    "\n",
    "This can quickly be done by leveraging a built-in library for Windows's users. This library is called the **Win32Com Library** and with it, we have a framework where we can use and publish our COM objects regardless of the language they were implemented in. **To be clear, with the Win32COM library we can access all sorts of different COM objects on our system, not just Office Applications. For example, we can access Adobe Applications, Visual Studios, and many more.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# A Simple Example With Excel\n",
    "\n",
    "Let's get a feel for the Win32COM library by walking through a simple example that works with the Excel Application from Python. This way, we can see how to call methods and properties of the different COM object and understand how tasks are taking place behind the scenes. This will not go into every single detail, but more set the stage for further discussion down the road.\n",
    "\n",
    "### Section One: Creating an Instance Of Excel\n",
    "The first thing we need to do is create an instance of our Excel application because we are assuming that we don't have an active instance running. The `Win32com.client` module makes this easy with the use of `Dispatch()` method, which creates a new COM object that will represent our Excel Application. \n",
    "\n",
    "How do we tell `Win32COM` which COM object to create? Well, there are two ways we can do this, either through the use of a ProgID or a CLISD. \n",
    "\n",
    "For completeness, here are the definitions for the CLSID and ProgID provided by Microsoft. **A programmatic identifier (ProgID)** is a registry entry that can be associated with a CLSID. Like the CLSID, the ProgID identifies a class but with less precision because it is not guaranteed to be globally unique.**The CLSID** is a globally unique identifier that identifies a COM class object. \n",
    "\n",
    "The main take away from this is that the CLSID is unique for every COM object, whereas the ProgID can technically appear more than once, for example, I could have multiple versions of Excel installed on my system.\n",
    "\n",
    "For Excel, the ProgID and CLSID appear as the following.\n",
    "\n",
    "- The **ProgId** being **Excel.Application**\n",
    "- The **CLSID** being **{00024500-0000-0000-C000-000000000046}**\n",
    "\n",
    "***\n",
    "\n",
    "Down below, I have provided both methods for creating an instance of the Excel Application. Now by default just because the object has been created doesn't mean we can see it. However, because we have a reference to the COM object now, we can start invoking different methods and properties. Let's set the `Visible` property to `True`, so it makes the application visible. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<win32com.gen_py.Microsoft Excel 16.0 Object Library._Application instance at 0x2575380955264>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# import the library\n",
    "import win32com.client\n",
    "\n",
    "# create an instance of Excel using the ProgID\n",
    "ExcelApp = win32com.client.Dispatch(\"Excel.Application\")\n",
    "ExcelApp.Visible = True\n",
    "\n",
    "# create an instance of Excel using the CLSID\n",
    "# ExcelAppCLSID = win32com.client.Dispatch(\"{00024500-0000-0000-C000-000000000046}\")\n",
    "# ExcelAppCLSID.Visible = True\n",
    "\n",
    "display(ExcelApp)\n",
    "# display(ExcelAppCLSID)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "***\n",
    "### Section Two: Add Objects To Our Application\n",
    "Okay we have our Excel Application, and it behaves exactly how it would behave in our VBA model, so let's add some more objects we can modify. The first object we will create is a Workbook object, this means we will go into our `ExcelApp`, call the `Workbooks` Collection property, and then the `Add()` method to add a new workbook to our collection. Let's do the same for a worksheet, but this time we have to go to our `ExcelWorkbook` object and then the `Worksheets` collection. We have to do this because the `Worksheets` collection exists in our `Workbook` object. Finally, let's define a range of cells and store them in a variable. From here, call the `Value` property and set the value of each cell to `200`.\n",
    "\n",
    "What exactly is happening behind the scenes for this to happen? Well we explore it in more detail later but at a high level, we are interacting with our **DispatchInterface** and asking it to provide some information in the form of different methods it makes available (GetIDsOfNames, Invoke). For example, when we added a new workbook it did something like the following:\n",
    "\n",
    "- **propertyIDForWorkbook** = ExcelApp -> GetIDsOfNames(\"Workbooks\")\n",
    "- **WorkbookCollectionObject** = ExcelApp -> Invoke(propertyIDForWorkbook, DISPATCH_PROPERTYGET)\n",
    "- **methodIDForAdd** = WorkbookCollectionObject -> GetIDsOfNames(\"Add\")\n",
    "- **AddedWorkbook** = WorkbookCollectionObject -> Invoke(methodIDForAdd, DISPATCH_METHOD)\n",
    "\n",
    "Don't worry if this seems confusing at first, we will implement this later, but hopefully, you caught on to a pattern. We ask if the property/method exists for that particular object using the **GetIDsOfNames** if it does return the ID, and finally invoke the method/property using the returned ID and **Invoke Method**.\n",
    "***"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<win32com.gen_py.None.Workbook>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "<win32com.gen_py.Microsoft Excel 16.0 Object Library._Worksheet instance at 0x2575387092752>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "<win32com.gen_py.Microsoft Excel 16.0 Object Library.Range instance at 0x2575387094040>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "((200.0,),\n",
       " (200.0,),\n",
       " (200.0,),\n",
       " (200.0,),\n",
       " (200.0,),\n",
       " (200.0,),\n",
       " (200.0,),\n",
       " (200.0,),\n",
       " (200.0,),\n",
       " (200.0,))"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#create a new workbook in the Excel app\n",
    "ExcelWorkbook = ExcelApp.Workbooks.Add()\n",
    "\n",
    "#create a new sheet in the workbook\n",
    "ExcelWrkSht = ExcelWorkbook.Worksheets.Add()\n",
    "\n",
    "# set a reference to a range of cells & then set their value to 200.\n",
    "ExcRng1 = ExcelWrkSht.Range(\"A1:A10\")\n",
    "ExcRng1.Value = 200\n",
    "\n",
    "display(ExcelWorkbook)\n",
    "display(ExcelWrkSht)\n",
    "display(ExcRng1)\n",
    "display(ExcRng1.Value)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "***\n",
    "### Section Three: Looping Through Cells\n",
    "Now that we have a range of cells let's explore how we can loop through them. Well, the first thing we need to recognize is that our large range of cells is made up of smaller individual cells that have their unique properties and methods. In this example, I'll define a second range of cells but this time using the `Cells` object. Once, I have my range of cells; I'll define the value of those cells using the `Value` property. From here, we will print out some info about our range of cells, like the number of cells in that particular range. Then we will use a for loop to loop through each cell, display the address, or display the value of that cell. I also chose, to apply to separate loops because I want to drive home the concept that you can use either loop through each cell or define a range of numbers that goes from 0 to the number of cells in our range.\n",
    "***"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "7\n",
      "$D$1\n",
      "$D$2\n",
      "$D$3\n",
      "$D$4\n",
      "$D$5\n",
      "$D$6\n",
      "$D$7\n",
      "100.0\n",
      "100.0\n",
      "100.0\n",
      "100.0\n",
      "100.0\n",
      "100.0\n",
      "100.0\n"
     ]
    }
   ],
   "source": [
    "# set a reference to a range of cells\n",
    "Cell1 = ExcelWrkSht.Cells(1,4)\n",
    "Cell2 = ExcelWrkSht.Cells(7,4)\n",
    "ExcRng2 = ExcelWrkSht.Range(Cell1,Cell2)\n",
    "ExcRng2.Value = 100\n",
    "\n",
    "# count the cells in our range\n",
    "print(ExcRng2.Cells.Count)\n",
    "\n",
    "# get the cell address using a for loop\n",
    "for cellItem in ExcRng2:\n",
    "    print(cellItem.Address)\n",
    "    \n",
    "# get the cell value using a for loop but this time using a range object.    \n",
    "for i in range(len(ExcRng2)):\n",
    "    print(cellItem.Value)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Interfaces\n",
    "\n",
    "Remember up above where we talked about how we interact with COM objects? It was through the use of Interfaces, in this section we will cover interfaces in more detail. The interface is simple terms, describes how an object will behave, however, it is up to the object itself to implement the behavior. There are multiple types of interfaces, the base interface that derives all other interfaces is the `IUnkown` interace. If we want to return a specific interface we can use the `QueryInterface()` method to return a specific interface. This is done through the use of a IID, which is a unique 128-bit GUID known as an interface ID (IID) that is unique for each COM interface.\n",
    "\n",
    "To meet the requirements of higher-level languages, the IDispatch interface was defined to allow the exposure of an object model that contain both the methods and properties of an object and defines a mechanism that will allow us to determine which methods and properties are available during runtime.\n",
    "\n",
    "I do want to note here, that IDispatch is simply the tool that is exposing the object model, which is it's own object separate from the IDispatch interface. There are two methods that allow this to take place, `GetIDsOfNames` and `Invoke()`. The `GetIDsOfNames` allows us to ask a given object whether a they have a given property or method. If it does, then it returns an integer ID for the method or property.\n",
    "\n",
    "With the ID we can then use the `Invoke()` method to pass through the ID and then invoke the action which is either calling the method or getting/setting the property.\n",
    "\n",
    "Let's see how this would all look in code and also cover a few of the remaining topics that we need to know with interfaces.\n",
    "\n",
    "***\n",
    "Okay, first thing we are going to do is impor the `pythoncom` which is a module, encapsulating the OLE automation API. The API is a feature that programs use to expose their objects to development tools, macro languages, and other programs that support Automation.\n",
    "\n",
    "From here, we will call the `new()` which creates a new instance of an OLE automation server, in this case Excel. Finally, we display the type of object which is a PyIDispatch, a python version of an IDispatch interface for our OLE automation client."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "PyIDispatch"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import pythoncom\n",
    "\n",
    "# create a new Excel COM object.\n",
    "xlCom = pythoncom.connect('Excel.Application')\n",
    "\n",
    "# let's see what type of Object it is\n",
    "display(type(xlCom))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Okay so we have an IDispatch interface object which exposes the object model for our Excel Application. Let's start by seeing what methods this PyIDispatch object has."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on PyIDispatch object:\n",
      "\n",
      "class PyIDispatch(PyIUnknown)\n",
      " |  Define the behavior of a PythonCOM Interface type.\n",
      " |  \n",
      " |  Method resolution order:\n",
      " |      PyIDispatch\n",
      " |      PyIUnknown\n",
      " |      interface-type\n",
      " |      object\n",
      " |  \n",
      " |  Methods defined here:\n",
      " |  \n",
      " |  GetIDsOfNames(...)\n",
      " |  \n",
      " |  GetTypeInfo(...)\n",
      " |  \n",
      " |  GetTypeInfoCount(...)\n",
      " |  \n",
      " |  Invoke(...)\n",
      " |  \n",
      " |  InvokeTypes(...)\n",
      " |  \n",
      " |  __delattr__(self, name, /)\n",
      " |      Implement delattr(self, name).\n",
      " |  \n",
      " |  __eq__(self, value, /)\n",
      " |      Return self==value.\n",
      " |  \n",
      " |  __ge__(self, value, /)\n",
      " |      Return self>=value.\n",
      " |  \n",
      " |  __getattribute__(self, name, /)\n",
      " |      Return getattr(self, name).\n",
      " |  \n",
      " |  __gt__(self, value, /)\n",
      " |      Return self>value.\n",
      " |  \n",
      " |  __le__(self, value, /)\n",
      " |      Return self<=value.\n",
      " |  \n",
      " |  __lt__(self, value, /)\n",
      " |      Return self<value.\n",
      " |  \n",
      " |  __ne__(self, value, /)\n",
      " |      Return self!=value.\n",
      " |  \n",
      " |  __repr__(self, /)\n",
      " |      Return repr(self).\n",
      " |  \n",
      " |  __setattr__(self, name, value, /)\n",
      " |      Implement setattr(self, name, value).\n",
      " |  \n",
      " |  ----------------------------------------------------------------------\n",
      " |  Data and other attributes defined here:\n",
      " |  \n",
      " |  __hash__ = None\n",
      " |  \n",
      " |  ----------------------------------------------------------------------\n",
      " |  Methods inherited from PyIUnknown:\n",
      " |  \n",
      " |  QueryInterface(...)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# let's take a look at our PyIDispatch Object.\n",
    "help(xlCom)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "***\n",
    "There are a few methods here, but I only want to talk about a few of them, and then we will come back to the rest when we need additional information about our object. The first one we will discuss is `GetIDsOfNames` which takes in a string that is either is a property or method of our object. If there is one, it returns the DISPID, which is the ID for that specific method or property. Formal documentation can be found here:\n",
    "\n",
    "- *Link to Win32COM:* http://timgolden.me.uk/pywin32-docs/PyIDispatch__GetIDsOfNames_meth.html\n",
    "- *Link to Microsoft:* https://docs.microsoft.com/en-us/windows/desktop/api/oaidl/nf-oaidl-idispatch-getidsofnames\n",
    "\n",
    "***\n",
    "\n",
    "## GetIDsOfNames:\n",
    "\n",
    "The (int, ...) or int = GetIDsOfNames(name)\n",
    "\n",
    "**Description**\n",
    "Get the DISPID for the passed names.\n",
    "\n",
    "**Parameters**\n",
    "- *name:* string or `[name1, name2, name3]`\n",
    "- *desc:* A name to query for\n",
    "\n",
    "**Comments**\n",
    "Currently, the LCID cannot be specified, and LOCALE_SYSTEM_DEFAULT is used.\n",
    "\n",
    "**Return Value**\n",
    "If the first parameter is a sequence, the result will be a tuple of integers for each name in the series. If the first parameter is a single string, the result is a single integer with the ID of the requested item."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "558"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "302"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# let's grab the visible property\n",
    "visible_id = xlCom.GetIDsOfNames('Visible')\n",
    "display(visible_id)\n",
    "\n",
    "# let's grab the Quit method.\n",
    "quit_id = xlCom.GetIDsOfNames('Quit')\n",
    "display(quit_id)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Okay so we have an IDispatch interface object which exposes the object model for our Excel Application, and we have a few IDs we can pass along in our Invoke methods, so let's start invoking methods and properties and seeing what we get back. The PyIDisptach has two different invoke methods, `Invoke` and `InvokeTypes`, there isn't a difference between these two other than the fact that with `InvokeTypes` you can define the return type. You'll see this later when talking about early versus late binding in COM. As always, here is some documentation:\n",
    "\n",
    "- *Link to Win32COM:* http://timgolden.me.uk/pywin32-docs/PyIDispatch__Invoke_meth.html\n",
    "- *Link to Microsoft:* https://docs.microsoft.com/en-us/windows/desktop/api/oaidl/nf-oaidl-idispatch-invoke\n",
    "\n",
    "\n",
    "## InvokeTypes\n",
    "\n",
    "**Syntax:** expression.InvokeTypes(dispid, lcid , wFlags , resultTypeDesc , typeDescs , args )\n",
    "\n",
    "**Description** Invokes a DISPID, using the passed arguments and type descriptions.\n",
    "\n",
    "***\n",
    "***\n",
    "**Parameters**\n",
    "\n",
    "**name:** dispid\n",
    "<br></br>\n",
    "**type:** int\n",
    "<br></br>\n",
    "**desc:** The dispid to use. Typically this value will come from PyIDispatch::GetIDsOfNames or from a type library.\n",
    "<br></br>\n",
    "\n",
    "**name:** lcid\n",
    "<br></br>\n",
    "**type:** int\n",
    "<br></br>\n",
    "**desc:** The locale id to use.\n",
    "<br></br>\n",
    "\n",
    "**name:** wflags\n",
    "<br></br>\n",
    "**type:** int\n",
    "<br></br>\n",
    "**desc:** The flags for the call. The following flags can be used.\n",
    "<br></br>\n",
    "\n",
    "**name:** resultTypeDesc\n",
    "<br></br>\n",
    "**type:** tuple\n",
    "<br></br>\n",
    "**desc:** A tuple describing the type of the result.\n",
    "<br></br>\n",
    "\n",
    "**name:** typeDescs\n",
    "<br></br>\n",
    "**type:** tuple\n",
    "<br></br>\n",
    "**desc:** A sequence of tuples describing the types of the parameters for the function. See the comments for more information.\n",
    "<br></br>\n",
    "\n",
    "**name:** args\n",
    "<br></br>\n",
    "**type:** object\n",
    "<br></br>\n",
    "**desc:** The arguments to be passed.\n",
    "<br></br>\n",
    "\n",
    "***\n",
    "***\n",
    "\n",
    "*Comments:* The Microsoft documentation for IDispatch should be used for all params except `resultTypeDesc` and `typeDescs`.\n",
    "\n",
    "**name:** resultTypeDesc\n",
    "<br></br>\n",
    "**desc:** describes the return value of the function, and is a tuple of (type_id, flags).\n",
    "<br></br>\n",
    "\n",
    "**name:** typeDescs\n",
    "<br></br>\n",
    "**desc:** describes the type of each parameters, and is a list of the same (type_id, flags) tuple.\n",
    "<br></br>\n",
    "\n",
    "***\n",
    "    item: type_id\n",
    "    desc: A valid \"variant type\" constant (eg, VT_I4 | VT_ARRAY, VT_DATE, etc - see VARIANT at MSDN).\n",
    "\n",
    "    item: flags\n",
    "    desc: One of the PARAMFLAG constants (e.g., PARAMFLAG_FIN, PARAMFLAG_FOUT, etc. - see PARAMFLAG at MSDN).\n",
    "***\n",
    "***\n",
    "\n",
    "**Return Value:** object"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "197\n",
      "0\n",
      "2\n",
      "(9, 0)\n",
      "((12, 1), (12, 1))\n",
      "(197, 0, 2, (9, 0), ((12, 1), (12, 1)), 'A1', 'A2')\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<PyIDispatch at 0x000002579B9558E0 with obj at 0x000002579F918C28>"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# does the Excel Application have a property called \"Range\"?\n",
    "RangeID = xlCom.GetIDsOfNames('Range')\n",
    "print(RangeID)\n",
    "\n",
    "# define the LCID (The locale id) to use.\n",
    "LCID = 0x0\n",
    "print(LCID)\n",
    "\n",
    "# define the flags for call, in this case we are getting a property so we will use DISPATCH_PROPERTYGET\n",
    "wFlags = pythoncom.DISPATCH_PROPERTYGET\n",
    "print(wFlags)\n",
    "\n",
    "# define the resultTypeDesc, this particular method returns a dispatch object (pythoncom.VT_DISPATCH) and \n",
    "# and the type of the contained field is undefined (pythoncom.VT_EMPTY). \n",
    "resultTypeDesc = (pythoncom.VT_DISPATCH, pythoncom.VT_EMPTY)\n",
    "print(resultTypeDesc)\n",
    "\n",
    "# define the type descriptions of the arguments we are passing in.\n",
    "# 2 arguments - given by two tuples\n",
    "#               first and second argument will be a variant (pythoncom.VT_VARIANT), and the parameter passes \n",
    "#               information from the caller to the callee pythoncom.PARAMFLAG_FIN\n",
    "typeDescs = ((pythoncom.VT_VARIANT, pythoncom.PARAMFLAG_FIN), (pythoncom.VT_VARIANT, pythoncom.PARAMFLAG_FIN))\n",
    "print(typeDescs)\n",
    "\n",
    "# define our two arguments, the range property takes two arguments cell1 and cell.\n",
    "cell1 = 'A1'\n",
    "cell2 = 'A2' \n",
    "\n",
    "# print everything together.\n",
    "print((RangeID, LCID, wFlags, resultTypeDesc, typeDescs, cell1, cell2))\n",
    "\n",
    "# grab the range object\n",
    "xlCom.InvokeTypes(RangeID, LCID, wFlags, resultTypeDesc, typeDescs, cell1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "***\n",
    "Okay so in that example we were working with a property, how would it look if we wanted to use a method? Well, for the most part, the structure is precisely the same; we just have to change the arguments."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# let's grab the DISPID of the Undo method.\n",
    "UndoID = xlCom.GetIDsOfNames('Undo')\n",
    "\n",
    "# define the LCID (The locale id) to use.\n",
    "LCID = 0x0\n",
    "\n",
    "# define the flags for call, in this case we are using a method so we will use DISPATCH_METHOD\n",
    "wFlags = pythoncom.DISPATCH_METHOD\n",
    "\n",
    "# define the resultTypeDesc, this particular method does not return anything (pythoncom.VT_VOID or 24) and \n",
    "# and the parameter passes information from the caller to the callee (pythoncom.PARAMFLAG_FIN or 1).\n",
    "# could also be written as (pythoncom.VT_VOID, pythoncom.PARAMFLAG_FIN)\n",
    "resultTypeDesc = (24, 1) \n",
    "\n",
    "# proof they are the same.\n",
    "display((pythoncom.VT_VOID, pythoncom.PARAMFLAG_FIN))\n",
    "\n",
    "# there are no arguments to pass through in this example, so we don't need to define their types\n",
    "# so we will pass through an empty tuple.\n",
    "typeDescs = ()\n",
    "\n",
    "# no arguments - SIMPLY A PLACE HOLDER\n",
    "args = ''\n",
    "\n",
    "# Let's put all the pieces together and invoke the method.\n",
    "xlCom.InvokeTypes(UndoID, LCID, wFlags, resultTypeDesc, typeDescs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here is an example of using the `Invoke` method using the `PyIDispatch` interface.\n",
    "\n",
    "\n",
    "# Invoke\n",
    "\n",
    "- Win32COM Documentation: http://timgolden.me.uk/pywin32-docs/PyIDispatch__Invoke_meth.html\n",
    "- Microsoft Documentation: https://docs.microsoft.com/en-us/windows/desktop/api/oaidl/nf-oaidl-idispatch-invoke\n",
    "\n",
    "**Syntax** expression.Invoke(dispid, lcid , flags , bResultWanted , params, ... )\n",
    "\n",
    "**Description** Invokes a DISPID, using the passed arguments.\n",
    "\n",
    "***\n",
    "***\n",
    "**Parameters**\n",
    "\n",
    "**name:** dispid\n",
    "<br></br>\n",
    "**type:** int\n",
    "<br></br>\n",
    "**desc:** The dispid to use. Typically this value will come from PyIDispatch::GetIDsOfNames or from a type library.\n",
    "<br></br>\n",
    "\n",
    "**name:** lcid\n",
    "<br></br>\n",
    "**type:** int\n",
    "<br></br>\n",
    "**desc:** The locale id to use.\n",
    "<br></br>\n",
    "\n",
    "**name:** flags\n",
    "<br></br>\n",
    "**type:** int\n",
    "<br></br>\n",
    "**desc:** The flags for the call. The following flags can be used.\n",
    "\n",
    "**name:** bResultWanted \n",
    "<br></br>\n",
    "**type:** int\n",
    "<br></br>\n",
    "**desc:** Indicates if the result of the call should be requested.\n",
    "<br></br>\n",
    "\n",
    "**name:** params\n",
    "<br></br>\n",
    "**type:** object\n",
    "<br></br>\n",
    "**desc:** The parameters to pass.\n",
    "\n",
    "***\n",
    "***\n",
    "\n",
    "*Comments:* If the bResultWanted parameter is False, then the result will be None. Otherwise, the result is determined by the COM object itself (and may still be None)\n",
    "\n",
    "**Return Value:** object"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# let's grab the DISPID of the Range Property.\n",
    "RangeID = xlCom.GetIDsOfNames('Range')\n",
    "\n",
    "# define the LCID (The locale id) to use. We just need to define the LOCALUSER\n",
    "LCID = 0x0\n",
    "\n",
    "# define the flags for call, in this case we are using a property so we will use DISPATCH_PROPERTYGET\n",
    "wFlags = pythoncom.DISPATCH_PROPERTYGET\n",
    "\n",
    "# do we want the results back? False means we get NONE True means we get the Result back if there is one.\n",
    "bResultWanted = True\n",
    "\n",
    "# no parameters to pass - SIMPLY A PLACE HOLDER\n",
    "cell1 = 'A1'\n",
    "cell2 = 'A2'\n",
    "\n",
    "# Let's put all the pieces together and invoke the method.\n",
    "myRangeObject = xlCom.Invoke(RangeID, LCID, wFlags, bResultWanted, cell1, cell2)\n",
    "display(myRangeObject)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here are the possible flags you can set for the calls.\n",
    "\n",
    "***\n",
    "**name:** DISPATCH_METHOD\n",
    "<br></br>\n",
    "**desc:** The member is invoked as a method. If a property has the same name, both this and the DISPATCH_PROPERTYGET flag may be set.\n",
    "<br></br>\n",
    "\n",
    "**name:** DISPATCH_PROPERTYGET\n",
    "<br></br>\n",
    "**desc:** The member is retrieved as a property or data member.\n",
    "<br></br>\n",
    "\n",
    "**name:** DISPATCH_PROPERTYPUT\n",
    "<br></br>\n",
    "**desc:** The member is changed as a property or data member.\n",
    "<br></br>\n",
    "\n",
    "**name:** DISPATCH_PROPERTYPUTREF\n",
    "<br></br>\n",
    "**desc:** The member is changed by a reference assignment, rather than a value assignment. This flag is valid only when the property accepts a reference to an object.\n",
    "<br></br>\n",
    "***"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you were like me, you saw the code up above, and you asked the question, \"Well that's great and all, but what if I don't know the property or methods of the object? What if I want to see all the methods and properties?\" Well, there is a way to see all the methods and properties of an object, but it requires us using a different method of our PyIDispatch interface object. The `GetTypeInfo` method returns an `PyITypeInfo` Object which contains information about the COM object. Getting the ITypeInfo object is quite easy.\n",
    "\n",
    "With this object, we can work with a few of the methods to get additional information about our COM object. The first one we will need to use is the `GetTypeAttr` method, which returns a `TypeAttr` object that contains different information about our COM object."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on PyITypeInfo object:\n",
      "\n",
      "class PyITypeInfo(PyIUnknown)\n",
      " |  Define the behavior of a PythonCOM Interface type.\n",
      " |  \n",
      " |  Method resolution order:\n",
      " |      PyITypeInfo\n",
      " |      PyIUnknown\n",
      " |      interface-type\n",
      " |      object\n",
      " |  \n",
      " |  Methods defined here:\n",
      " |  \n",
      " |  GetContainingTypeLib(...)\n",
      " |  \n",
      " |  GetDocumentation(...)\n",
      " |  \n",
      " |  GetFuncDesc(...)\n",
      " |  \n",
      " |  GetIDsOfNames(...)\n",
      " |  \n",
      " |  GetImplTypeFlags(...)\n",
      " |  \n",
      " |  GetNames(...)\n",
      " |  \n",
      " |  GetRefTypeInfo(...)\n",
      " |  \n",
      " |  GetRefTypeOfImplType(...)\n",
      " |  \n",
      " |  GetTypeAttr(...)\n",
      " |  \n",
      " |  GetTypeComp(...)\n",
      " |  \n",
      " |  GetVarDesc(...)\n",
      " |  \n",
      " |  __delattr__(self, name, /)\n",
      " |      Implement delattr(self, name).\n",
      " |  \n",
      " |  __eq__(self, value, /)\n",
      " |      Return self==value.\n",
      " |  \n",
      " |  __ge__(self, value, /)\n",
      " |      Return self>=value.\n",
      " |  \n",
      " |  __getattribute__(self, name, /)\n",
      " |      Return getattr(self, name).\n",
      " |  \n",
      " |  __gt__(self, value, /)\n",
      " |      Return self>value.\n",
      " |  \n",
      " |  __le__(self, value, /)\n",
      " |      Return self<=value.\n",
      " |  \n",
      " |  __lt__(self, value, /)\n",
      " |      Return self<value.\n",
      " |  \n",
      " |  __ne__(self, value, /)\n",
      " |      Return self!=value.\n",
      " |  \n",
      " |  __repr__(self, /)\n",
      " |      Return repr(self).\n",
      " |  \n",
      " |  __setattr__(self, name, value, /)\n",
      " |      Implement setattr(self, name, value).\n",
      " |  \n",
      " |  ----------------------------------------------------------------------\n",
      " |  Data and other attributes defined here:\n",
      " |  \n",
      " |  __hash__ = None\n",
      " |  \n",
      " |  ----------------------------------------------------------------------\n",
      " |  Methods inherited from PyIUnknown:\n",
      " |  \n",
      " |  QueryInterface(...)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Grab the Type Info\n",
    "xlTypeInfo = xlCom.GetTypeInfo()\n",
    "\n",
    "# let's get some info about our TypeInfo Object. This will be covered in an additional Video\n",
    "help(xlTypeInfo)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "With TypeAttr object we can now call the attributes of our object, I've put a list of all the properties that exist in the TypeAttr object if you want you can always call `help(xlTypeAttr)` to see the docstring."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'----------------------------------------------------------------------------------------------------'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'The IID'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "IID('{000208D5-0000-0000-C000-000000000046}')"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'----------------------------------------------------------------------------------------------------'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'The LCID'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'----------------------------------------------------------------------------------------------------'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'The ID of the Constructor'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "-1"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'----------------------------------------------------------------------------------------------------'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'The ID of the Destructor '"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "-1"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'----------------------------------------------------------------------------------------------------'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'The cbSizeInstance'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "8"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'----------------------------------------------------------------------------------------------------'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'The TypeKind'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'----------------------------------------------------------------------------------------------------'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'The Number of Functions'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "467"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'----------------------------------------------------------------------------------------------------'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'The Number of Variables/Data members'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'----------------------------------------------------------------------------------------------------'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'The Number of Implemented Interfaces'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'----------------------------------------------------------------------------------------------------'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'The cbSizeVft'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "56"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'----------------------------------------------------------------------------------------------------'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'The cbAlignment'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "8"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'----------------------------------------------------------------------------------------------------'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'The wTypeFlags'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "4160"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'----------------------------------------------------------------------------------------------------'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'The Major Version Number'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'----------------------------------------------------------------------------------------------------'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'The Minor Version Number'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'----------------------------------------------------------------------------------------------------'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'The tdescAliar'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "None"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'----------------------------------------------------------------------------------------------------'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'The IDL Attributes'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "(0, 0)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# the TypeInfo object contains the GetTypeAttr, callthe GetTypeAttr() method to return the object.\n",
    "xlTypeAttr = xlTypeInfo.GetTypeAttr()\n",
    "\n",
    "# The IID\n",
    "# At Index 0\n",
    "display('-'*100)\n",
    "display('The IID')\n",
    "display(xlTypeAttr.iid)\n",
    "\n",
    "# The LCID\n",
    "# At Index 1\n",
    "display('-'*100)\n",
    "display('The LCID')\n",
    "display(xlTypeAttr.lcid)\n",
    "\n",
    "# The ID of Constructor\n",
    "# At Index 2\n",
    "display('-'*100)\n",
    "display('The ID of the Constructor')\n",
    "display(xlTypeAttr.memidConstructor)\n",
    "\n",
    "# The ID of Destructor\n",
    "# At Index 3\n",
    "display('-'*100)\n",
    "display('The ID of the Destructor ')\n",
    "display(xlTypeAttr.memidDestructor)\n",
    "\n",
    "# cbSizeInstance - The size of an instance of this type\n",
    "# At Index 4\n",
    "display('-'*100)\n",
    "display('The cbSizeInstance')\n",
    "display(xlTypeAttr.cbSizeInstance)\n",
    "\n",
    "# typekind - The kind of type this information describes. One of the win32con.TKIND_* constants.\n",
    "# At Index 5\n",
    "display('-'*100)\n",
    "display('The TypeKind')\n",
    "display(xlTypeAttr.typekind)\n",
    "\n",
    "# cFuncs - Number of functions.\n",
    "# At Index 6\n",
    "display('-'*100)\n",
    "display('The Number of Functions')\n",
    "display(xlTypeAttr.cFuncs)\n",
    "\n",
    "# cVars - Number of variables/data members.\n",
    "# At Index 7\n",
    "display('-'*100)\n",
    "display('The Number of Variables/Data members')\n",
    "display(xlTypeAttr.cVars)\n",
    "\n",
    "# cImplTypes - Number of implemented interfaces.\n",
    "# At Index 8\n",
    "display('-'*100)\n",
    "display('The Number of Implemented Interfaces')\n",
    "display(xlTypeAttr.cImplTypes)\n",
    "\n",
    "# cbSizeVft - The size of this type's VTBL\n",
    "# At Index 9\n",
    "display('-'*100)\n",
    "display('The cbSizeVft')\n",
    "display(xlTypeAttr.cbSizeVft)\n",
    "\n",
    "# cbAlignment - Byte alignment for an instance of this type.\n",
    "# At Index 10\n",
    "display('-'*100)\n",
    "display('The cbAlignment')\n",
    "display(xlTypeAttr.cbAlignment)\n",
    "\n",
    "# wTypeFlags - One of the pythoncom TYPEFLAG_* constants\n",
    "# At Index 11\n",
    "display('-'*100)\n",
    "display('The wTypeFlags')\n",
    "display(xlTypeAttr.wTypeFlags)\n",
    "\n",
    "# wMajorVerNum - Major version number.\n",
    "# At Index 12\n",
    "display('-'*100)\n",
    "display('The Major Version Number')\n",
    "display(xlTypeAttr.wMajorVerNum)\n",
    "\n",
    "# wMinorVerNum - Minor version number.\n",
    "# At Index 13\n",
    "display('-'*100)\n",
    "display('The Minor Version Number')\n",
    "display(xlTypeAttr.wMinorVerNum)\n",
    "\n",
    "# tdescAlias - If TypeKind == pythoncom.TKIND_ALIAS, specifies the type for which this type is an alias.\n",
    "# At Index 14\n",
    "display('-'*100)\n",
    "display('The tdescAliar')\n",
    "display(xlTypeAttr.tdescAlias)\n",
    "\n",
    "# idldescType - IDL attributes of the described type.\n",
    "# At Index 15\n",
    "display('-'*100)\n",
    "display('The IDL Attributes')\n",
    "display(xlTypeAttr.idldescType)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Out of all of these attributes, there is one that will be very helpful to explore as it will be used later down the road. The `cFuncs` attribute contains the number of functions in our COM object, and this is simply the number of methods and properties it has exposed to us."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'----------------------------------------------------------------------------------------------------'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "()"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "('Visible',\n",
       " None,\n",
       " 133229,\n",
       " 'C:\\\\Program Files\\\\Microsoft Office\\\\Root\\\\Office16\\\\VBAXL10.CHM')"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "558"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "('Visible',)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "()"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "2256"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "(11, 0, None)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'----------------------------------------------------------------------------------------------------'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "((11, 1, None),)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "('Visible',\n",
       " None,\n",
       " 133229,\n",
       " 'C:\\\\Program Files\\\\Microsoft Office\\\\Root\\\\Office16\\\\VBAXL10.CHM')"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "558"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "('Visible',)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "()"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "2264"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "(24, 0, None)"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# the information that we need lives in the TYPEATTR Object, so let's grab it.\n",
    "xlTypeAttr = xlTypeInfo.GetTypeAttr()\n",
    "\n",
    "# define the number of functions\n",
    "numFuncs =  xlTypeAttr.cFuncs\n",
    "\n",
    "# Let's grab all the Functions of a given object\n",
    "for i in range(0, numFuncs):    \n",
    "    \n",
    "    # first grab the func desc.\n",
    "    xlFuncDesc = xlTypeInfo.GetFuncDesc(i)\n",
    "    \n",
    "    if xlFuncDesc.memid == 558:\n",
    "    \n",
    "        display('-'*100)\n",
    "\n",
    "        # display the arguments of the function\n",
    "        display(xlFuncDesc.args)\n",
    "\n",
    "        # display the documenation string for that particular member id\n",
    "        display(xlTypeInfo.GetDocumentation(xlFuncDesc.memid))\n",
    "\n",
    "        # display the member ID\n",
    "        display(xlFuncDesc.memid)\n",
    "\n",
    "        # Get the name of the of function using the member ID\n",
    "        display(xlTypeInfo.GetNames(xlFuncDesc.memid))\n",
    "\n",
    "        # Indicates the type of function (virtual, static, or dispatch-only)\n",
    "        display(xlFuncDesc.funckind)\n",
    "\n",
    "        # The number of optional parameters.\n",
    "        display(xlFuncDesc.cParamsOpt)\n",
    "\n",
    "        # The function flags. \n",
    "        display(xlFuncDesc.wFuncFlags)\n",
    "\n",
    "        # The number of possible return values.\n",
    "        display(xlFuncDesc.scodeArray)\n",
    "\n",
    "        # For FUNC_VIRTUAL, specifies the offset in the VTBL.\n",
    "        display(xlFuncDesc.oVft)\n",
    "\n",
    "        # The invocation type. Indicates whether this is a property function, and if so, which type.\n",
    "        display(xlFuncDesc.invkind)\n",
    "\n",
    "        # The calling convention.\n",
    "        display(xlFuncDesc.callconv)\n",
    "\n",
    "        # The return type?\n",
    "        display(xlFuncDesc.rettype)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "I only took on of the items returned by FuncDesc method; in this case, I chose the `Visible` property. Let's dissect what was sent back to us.\n",
    "\n",
    "**Parameter Name:** args\n",
    "<br></br>\n",
    "**Return Value:** ((11, 1, None),)\n",
    "<br></br>\n",
    "**Description** For this particular method/property, it takes a VT_BOOLEAN argument (11) the type description, takes PARAMFLAG_FIN (1) as an argument which is the idlFlag, and if PARAMFLAG_FHASDEFAULT are set, then this is the default value otherwise it's none.\n",
    "\n",
    "**Parameter Name:** memid\n",
    "<br></br>\n",
    "**Return Value:** 558\n",
    "<br></br>\n",
    "**Description:** This is the DISPID for the specified property/method.\n",
    "<br></br>\n",
    "\n",
    "**Parameter Name:** funcKind\n",
    "<br></br>\n",
    "**Return Value:** 4\n",
    "<br></br>\n",
    "**Description:** Indicates the type of function (virtual, static, or dispatch-only).\n",
    "<br></br>\n",
    "\n",
    "***\n",
    "***\n",
    "    FUNC_DISPATCH   4    \n",
    "    pythoncom.FUNC_DISPATCH\n",
    "    The function can be accessed only through IDispatch.\n",
    "\n",
    "    FUNC_STATIC     3    \n",
    "    pythoncom.FUNC_STATIC\n",
    "    The function is accessed by static address and does not take an implicit this pointer.\n",
    "\n",
    "    FUNC_VIRTUAL    0    \n",
    "    pythoncom.FUNC_VIRTUAL\n",
    "    The function is accessed in the same way as FUNC_PUREVIRTUAL, except the function has an implementation.\n",
    "***\n",
    "***\n",
    "\n",
    "**Parameter Name:** cParamsOpt\n",
    "<br></br>\n",
    "**Return Value:** 0\n",
    "<br></br>\n",
    "**Description:** The number of optional parameters\n",
    "<br></br>\n",
    "\n",
    "**Parameter Name:** wFuncFlags\n",
    "<br></br>\n",
    "**Return Value:** 0\n",
    "<br></br>\n",
    "**Description:** The function flags\n",
    "<br></br>\n",
    "\n",
    "**Parameter Name:** scodeArray\n",
    "<br></br>\n",
    "**Return Value:** ()\n",
    "<br></br>\n",
    "**Description:** The number of possible return values.\n",
    "<br></br>\n",
    "\n",
    "**Parameter Name:** ovft\n",
    "<br></br>\n",
    "**Return Value:** 2256\n",
    "<br></br>\n",
    "**Description:** The function flags\n",
    "<br></br>\n",
    "\n",
    "**Parameter Name:** invkind\n",
    "<br></br>\n",
    "**Return Value:** 2\n",
    "<br></br>\n",
    "**Description:** The invocation type. Indicates whether this is a property function, and if so, which type. In this case it's a INVOKE_PROPERTYGET (2)\n",
    "<br></br>\n",
    "\n",
    "***\n",
    "***\n",
    "    INVOKE_PROPERTYGET      2    \n",
    "    pythoncom.INVOKE_PROPERTYGET\n",
    "\n",
    "    INVOKE_FUNC             1    \n",
    "    pythoncom.INVOKE_FUNC\n",
    "\n",
    "    INVOKE_PROPERTYPUT      4    \n",
    "    pythoncom.INVOKE_PROPERTYPUT\n",
    "    \n",
    "    INVOKE_PROPERTYPUTREF   8    \n",
    "    pythoncom.INVOKE_PROPERTYPUTREF\n",
    "***\n",
    "***\n",
    "\n",
    "**Parameter Name:** callconv\n",
    "<br></br>\n",
    "**Return Value:** 4\n",
    "<br></br>\n",
    "**Description:** The calling convention. In this case I believe this would be a STDCALL (4).\n",
    "<br></br>\n",
    "\n",
    "**Parameter Name:** rettype\n",
    "<br></br>\n",
    "**Return Value:** (11, 0, None)\n",
    "<br></br>\n",
    "**Description:** The return type which in this case is a VT_BOOLEAN (11), and has a IDLFlag of PARAMFLAG_NONE (0), finally does not return an object (None)\n",
    "<br></br>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Okay, so we now know how to get all the information we need to invoke any method or property we need. This is very useful, and in fact, these different objects are leveraged inside the Win32COM modules directly when it comes to auto generating Python code that will be used for early binding in Win32COM.\n",
    "\n",
    "# Early Versus Late Binding\n",
    "\n",
    "This leads to our next topic, early versus late binding inside of Win32COM. What is the difference between these two methods, well in simple terms when we are talking about early versus late binding all we are talking about is whether the programming language we are using has any upfront knowledge of the object. In other words, does the programming language know any of the methods and properties of the object at hand? If we do have upfront knowledge, we know all the methods and properties of the object; then the object was implemented using early-binding. If we don't have any upfront knowledge, we know none of the methods or properties of the object; then the object was implemented using late binding.\n",
    "\n",
    "By default,the `win32com.client` package uses late-bound automation when using objects. Here is a simple way to tell if the object was implemented using late-binding."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<COMObject PowerPoint.Application>"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import win32com.client\n",
    "\n",
    "# Dispatch a new COM Object\n",
    "PPTApp = win32com.client.Dispatch('PowerPoint.Application')\n",
    "#3E65FF\n",
    "# display it to the user\n",
    "PPTApp"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see, all Python knows is that the object is a COM Object and that it has a Program ID called `PowerPoint.Application`. However, this is all that Python knows about the object, not any of the methods or properties.\n",
    "\n",
    "If we want, we can make Python have upfront knowledge of our COM object, but it will require us using early-bound automation. **Now, this is easy to do in Python because we call the `gencache` module and then the `EnsureDispatch` method inside that module.** However, a lot of the heavy lifting to make early-bound automation possible is done behind the scenes without us even knowing it. To give you a better understanding of this module, I think it pays dividends to explore what happens during this process.\n",
    "\n",
    "Inside the PythonCOM module, there is a script called MakePy, which is a utility to support early-bound automation. This script leverages the COM Type Library object explored up above to generate Python source code that supports the COM object it was called on. In other words, this utility goes out and collects all the information on our COM object, converts it into Python source code, and stores it in individual scripts that we can call using the `win32com` library.\n",
    "\n",
    "You might ask, why would I want to use early-bound automation? \n",
    "\n",
    "1. It's faster for the Python Interface\n",
    "2. Constants defined by the Type Library (xlDown) are made available to the Python program. This makes our code a lot more readable.\n",
    "\n",
    "Now that we know the advantages of using early-bound automation let's see how to call the MakePy utility. Your first option is to use the PythonWin application, which is installed when you install the `win32` library for Python. Finding this application can be found in a few steps:\n",
    "\n",
    "1. Type \"PythonWin\" into your Windows' search bar.\n",
    "2. Open the application.\n",
    "3. Once open, navigate the \"Tool\" button on the navigation bar. A new window will open up.\n",
    "4. Select the Reference library you would like to call the MakePy utility on, and press Ok.\n",
    "5. The library will be made; any errors encountered will be printed on the terminal below.\n",
    "\n",
    "The second, and the more likely method you will use is to call the `gencache` module, and then the `EnsureDispatch()` function in that module. In whole, this how it would look:\n",
    "\n",
    "`xlApp = win32com.client.gencahce.EnsureDispatch('Excel.Application')`\n",
    "\n",
    "The `gencache` module runs the MakePy utility behind the scenes, so that early bound automation may be achieved. Pass through the CLSID or the ProgID. Regardless of which method you use, Python source code files will be created in each instance. To find these files, we can navigate to them. Just as a warning, I can't guarantee that the files will exist in the same location for every type of user this is simply the location that was leveraged on my system.\n",
    "\n",
    "When the MakePy utility is run, a new folder called `gen_py` is created in your Local folder. Here is the path to it on my system:\n",
    "\n",
    "`C:\\Users\\{YOUR NAME}\\AppData\\Local\\Temp\\gen_py`\n",
    "<br></br>\n",
    "\n",
    "Inside this folder is all the Python generated code made by the MakePy utility. You may find multiple folders, for example in mine there is a 3.5 & 3.7 folder, my best guess is that these are representing the different versions of Python on your system, but I can't say for sure. The generated python files exist in the 3.7 one for my system. I have seen some situations where people's numbering have been different.\n",
    "\n",
    "When I run the MakePy utility on the PowerPoint application, it generates a folder with the following path.\n",
    "\n",
    "`C:\\Users\\{YOUR NAME}\\AppData\\Local\\Temp\\gen_py\\3.7\\91493440-5A91-11CF-8700-00AA0060263Bx0x2x12`\n",
    "\n",
    "The folder name may look confusing, but it's just the CLSID for the given object, in this case, the PowerPoint application. Inside the folder, depending on the object, you will see multiple python scrips which contain all the information about the given objects that exist in that particular object hierarchy. For example, in mine, there is one for the `Shapes` collection and the `Presentations` collection.\n",
    "\n",
    "If you open up the `_Presentations.py` script, you'll see all the auto-generated code for that object. Here is just a small portion of that code."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "CLSID = IID('{91493440-5A91-11CF-8700-00AA0060263B}')\n",
    "MajorVersion = 2\n",
    "MinorVersion = 12\n",
    "LibraryFlags = 8\n",
    "LCID = 0x0\n",
    "\n",
    "class _Presentation(DispatchBaseClass):\n",
    "    \n",
    "    CLSID = IID('{9149349D-5A91-11CF-8700-00AA0060263B}')\n",
    "    coclass_clsid = IID('{91493444-5A91-11CF-8700-00AA0060263B}')\n",
    "\n",
    "    def AcceptAll(self):\n",
    "        return self._oleobj_.InvokeTypes(2115, LCID, 1, (24, 0), (),)\n",
    "\n",
    "    def AddBaseline(self, FileName=''):\n",
    "        return self._ApplyTypes_(2073, 1, (24, 32), ((8, 49),), 'AddBaseline', None, FileName)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The first thing you should notice is that the MakePy utility makes a class object for each automation object in the Type library. The one you're currently looking at is the `_Presentation` object, inside this class object are all the methods and properties belonging to the said object. \n",
    "\n",
    "For example, the `AcceptAll` method accepts all the changes in the presentation. Notice, how it is merely calling `InvokeTypes` method described up above? This is why we needed to cover the IDispatch interface! It's because it plays a crucial role in the development of the Python generated code. This way, when you look at the code, you won't be confused by all the abstract numbers.\n",
    "\n",
    "The same goes for `_ApplyTypes_`, it's directly calling the `InvokeTypes` method behind the scenes to either set or retrieve the information at hand. \n",
    "\n",
    "The other exciting part about the autogenerated code is the object properties, and if you scroll down further, you'll see, in some cases, a very comprehensive dictionary containing the all the `get_properties` and the `put_properties`. Here is how they look:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# here is a few of the Get Properties\n",
    "_prop_map_get_ = {\n",
    "    # Method 'Application' returns object of type 'Application'\n",
    "    \"Application\": (2001, 2, (13, 0), (), \"Application\", '{91493441-5A91-11CF-8700-00AA0060263B}'),\n",
    "    \"AutoSaveOn\": (2129, 2, (11, 0), (), \"AutoSaveOn\", None)\n",
    "}\n",
    "\n",
    "# here are a few of the Put properties\n",
    "_prop_map_put_ = {\n",
    "    \"AutoSaveOn\": ((2129, LCID, 4, 0),()),\n",
    "    \"ChartDataPointTrack\": ((2125, LCID, 4, 0),())\n",
    "}\n",
    "\n",
    "# You can access these attributes a few different ways\n",
    "AppProp1 = PPTApp.Presentations.Application\n",
    "AppProp2 = PPTApp.Presentations.__getattr__('Application')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's take some time and walk through two of the items, one from each dictionary. If you notice each key represents the property of the object, and the value associated with that key is simply a tuple of information. \n",
    "\n",
    "The first item, for the `_prop_map_get_` is the property id, the second number determines whether it is an INVOKE_PROPERTYGET (2) or INVOKE_PROPERTYPUT(4), the third is the return type description discussed up above, the fifth is the property name, and the final one specifies it doesn't return another object.\n",
    "\n",
    "The first item, for the `_prop_map_put_` is the property id, the second number is the local context id discussed up above, the third represents that it is an INVOKE_PROPERTYPUT(4), and the fourth, from my best guess, represents its `wFlags`.\n",
    "\n",
    "\n",
    "***\n",
    "***\n",
    "\n",
    "`(2129, 2, (11, 0), (), \"AutoSaveOn\", None)`\n",
    "\n",
    "***\n",
    "***\n",
    "\n",
    "`2129` - The MemId for AutoSaveOn\n",
    "<br></br>\n",
    "`2` - pythoncom.INVOKE_PROPERTYGET\n",
    "<br></br>\n",
    "`(11, 0)` - 11 returns a boolean and 0 pythoncom.PARAMFLAG_NONE\n",
    "<br></br>\n",
    "`\"AutoSaveOn\"` - Property Name\n",
    "<br></br>\n",
    "`None` - This property does not return an object\n",
    "\n",
    "***\n",
    "***\n",
    "\n",
    "`\"AutoSaveOn\": ((2129, LCID, 4, 0),())`\n",
    "\n",
    "***\n",
    "***\n",
    "\n",
    "`2129` - The MemId for AutoSaveOn\n",
    "<br></br>\n",
    "`LCID` - Local Context ID, define at the beginning of the file\n",
    "<br></br>\n",
    "`4` -  pythoncom.INVOKE_PROPERTYPUT\n",
    "<br></br>\n",
    "`0` -  pythoncom.PARAMFLAG_NONE\n",
    "\n",
    "***\n",
    "***"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "At this point, you should feel somewhat comfortable exploring a MakePy generate Python file. You understand that the object is represented as a Python class object, the methods belonging to that the class object translates into the methods of the COM Object.The properties exist in a dictionary that can be accessed by the `__getattr__()` or calling the property directly on the class object, and finally the methods leverage the `InvokeTypes()` to call the methods and properties.\n",
    "\n",
    "Regardless of whether you want to use late or early bound automation, you can force either one these by using `Dynamic` module, for late bound automation, and `gencahce` module, for early bound automation. The gencache runs the MakePy support that allows for early bound automation, and then the gencache calls the `EnsureDispatch` method to dispatch the object. Here is an example of forcing early and late bound automation.\n",
    "\n",
    "If you choose to use the regular `Dispatch()` method without specifying either the `dynamic` or `gencache` module, then the `Win32COM` library will choose the best option. Most of the times, it will use early-bound automation if the `gencache` module has been called on the object before."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'LATE BOUND'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "<COMObject PowerPoint.Application>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'EARLY BOUND'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "<win32com.gen_py.Microsoft PowerPoint 16.0 Object Library._Application instance at 0x1981722340544>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "'CHOOSE FOR US'"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "<win32com.gen_py.Microsoft PowerPoint 16.0 Object Library._Application instance at 0x1981719975808>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import win32com.client\n",
    "# late bound automation\n",
    "PPTApp_Late = win32com.client.dynamic.Dispatch('PowerPoint.Application')\n",
    "display('LATE BOUND')\n",
    "display(PPTApp_Late)\n",
    "\n",
    "# early bound automation\n",
    "PPTApp_Early = win32com.client.gencache.EnsureDispatch('PowerPoint.Application')\n",
    "display('EARLY BOUND')\n",
    "display(PPTApp_Early)\n",
    "\n",
    "# let win32COM choose\n",
    "PPTApp_Choose = win32com.client.Dispatch('PowerPoint.Application')\n",
    "display('CHOOSE FOR US')\n",
    "display(PPTApp_Choose)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
